06 Docker部署AI Agent实战
前面五篇把Docker的核心概念和工具都学完了。现在来做一个完整的实战——把一个Flask+Redis的Agent服务容器化部署。
这个项目是一个带访问计数的Agent API服务,Flask处理HTTP请求,Redis存储计数。虽然简单,但涵盖了Docker部署的完整流程:Dockerfile编写、Compose编排、健康检查、数据持久化、开发热更新。
一、项目结构
agent-api/
├── src/
│ └── main.py # Flask应用
├── requirements.txt # Python依赖
├── Dockerfile # 镜像构建配置
├── .dockerignore # 构建排除文件
├── .env # 环境变量
├── compose.yaml # 服务编排
└── infra.yaml # 基础设施服务二、应用代码
python
# src/main.py
import os
import redis
from flask import Flask, request, jsonify
app = Flask(__name__)
cache = redis.Redis(
host=os.getenv("REDIS_HOST", "redis"),
port=int(os.getenv("REDIS_PORT", "6379")),
)
@app.route("/")
def index():
count = cache.incr("hits")
return jsonify({"message": "Agent服务运行中", "visits": count})
@app.route("/health")
def health():
try:
cache.ping()
return jsonify({"status": "healthy"}), 200
except redis.ConnectionError:
return jsonify({"status": "unhealthy"}), 500
@app.route("/chat", methods=["POST"])
def chat():
data = request.get_json()
user_message = data.get("message", "")
count = cache.incr("chat_count")
return jsonify({
"reply": f"收到你的第{count}条消息: {user_message}",
"count": count,
})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, debug=True)text
# requirements.txt
flask
redis三、编写Dockerfile
dockerfile
FROM python:3.12-alpine
# 创建非root用户
RUN addgroup -S app && adduser -S app -G app
WORKDIR /app
# 先复制依赖文件,利用构建缓存
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
# 再复制源代码
COPY src ./src
# 设置环境变量
ENV PYTHONUNBUFFERED=1
# 切换到非root用户
USER app
EXPOSE 5000
CMD ["python", "src/main.py"]逐行拆解:
FROM python:3.12-alpine——用Alpine版本,镜像体积小- 创建非root用户——安全最佳实践
- 先COPY requirements.txt再COPY src——利用构建缓存,改代码不用重装依赖
PYTHONUNBUFFERED=1——让Python直接输出日志,不缓冲USER app——以非root用户运行
四、编写.dockerignore
plaintext
.git
.env
__pycache__
*.pyc
.venv
*.md
docker-compose*.yaml
compose.yaml
infra.yaml
Dockerfile排除不需要打包进镜像的文件,特别是.env(可能包含API Key)和.git(体积大且无用)。
五、编写compose.yaml
yaml
include:
- path: ./infra.yaml
services:
web:
build: .
ports:
- "${APP_PORT}:5000"
environment:
- REDIS_HOST=redis
- REDIS_PORT=6379
depends_on:
redis:
condition: service_healthy
develop:
watch:
- action: sync+restart
path: ./src
target: /app/src
- action: rebuild
path: requirements.txtyaml
# infra.yaml
services:
redis:
image: redis:alpine
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
start_period: 10s
volumes:
redis-data:text
# .env
APP_PORT=8000
REDIS_HOST=redis
REDIS_PORT=6379拆解每个配置项:
| 配置 | 作用 |
|---|---|
include: ./infra.yaml | 把基础设施服务拆到独立文件 |
build: . | 用当前目录的Dockerfile构建 |
ports: "${APP_PORT}:5000" | 端口映射,值从.env读取 |
depends_on + healthcheck | 等Redis就绪后再启动web |
develop.watch | 开发时代码热更新 |
volumes: redis-data | Redis数据持久化 |
六、运行和测试
6.1 首次启动
bash
docker compose up输出类似:
[+] Running 2/2
✔ Container agent-api-redis-1 Healthy 0.0s
✔ Container agent-api-web-1 Started 0.5sCompose等Redis健康检查通过后才启动web服务。
6.2 测试接口
bash
# 首页(带访问计数)
curl http://localhost:8000
# {"message":"Agent服务运行中","visits":1}
# 健康检查
curl http://localhost:8000/health
# {"status":"healthy"}
# Agent聊天接口
curl -X POST http://localhost:8000/chat \
-H "Content-Type: application/json" \
-d '{"message": "你好"}'
# {"count":1,"reply":"收到你的第1条消息: 你好"}6.3 验证数据持久化
bash
# 多访问几次,让计数增加
curl http://localhost:8000
curl http://localhost:8000
curl http://localhost:8000
# 停掉服务
docker compose down
# 重新启动
docker compose up -d
# 访问计数还在
curl http://localhost:8000
# {"message":"Agent服务运行中","visits":4}因为Redis数据存在Volume里,docker compose down不会删除Volume。
6.4 验证开发热更新
用docker compose up --watch启动:
bash
docker compose up --watch修改src/main.py里的返回消息:
python
return jsonify({"message": "Agent服务已更新", "visits": count})保存后Compose自动同步到容器并重启,刷新页面就能看到新内容,不需要手动重建镜像。
七、查看日志和调试
bash
# 查看所有服务日志
docker compose logs -f
# 只看web服务日志
docker compose logs -f web
# 进入web容器
docker compose exec web sh
# 查看环境变量
docker compose exec web env | grep REDIS
# 测试web能否连通redis
docker compose exec web python -c "import redis; r = redis.Redis(host='redis'); print(r.ping())"
# 输出: True
# 查看redis里的数据
docker compose exec redis redis-cli GET hits八、生产部署建议
这个实战项目是开发用的配置。生产部署还需要注意:
8.1 关闭Debug模式
python
# 生产环境不要用debug=True
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)用Gunicorn代替Flask内置服务器:
dockerfile
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "src.main:app"]8.2 不暴露Redis端口
生产环境Redis不需要对外暴露端口,只让web服务通过内部网络访问就行。
8.3 日志收集
yaml
services:
web:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"8.4 资源限制
yaml
services:
web:
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M九、总结
这个实战项目覆盖了Docker部署的核心流程:
| 步骤 | 用到的知识 |
|---|---|
| 编写Dockerfile | FROM、WORKDIR、COPY、RUN、ENV、USER、CMD |
| 构建缓存优化 | 先COPY依赖文件,再COPY源代码 |
| 安全最佳实践 | 非root用户、.dockerignore排除敏感文件 |
| Compose编排 | services、build、ports、environment |
| 服务依赖 | depends_on + healthcheck |
| 数据持久化 | Volume |
| 开发热更新 | Compose Watch |
| 环境管理 | .env文件 |
整个Docker系列到这里就结束了。从基础概念到Dockerfile编写,从镜像优化到Compose编排,从网络存储到实战部署——掌握这些内容,你就能独立完成一个AI Agent服务的容器化部署。